# encoding=utf-8
from abc import ABC

from wintersweet.utils.struct import ImmutableDict


class EnumDict(ImmutableDict):

    def __getitem__(self, item):
        if isinstance(item, EnumItem):
            item = item.value
        
        return super().__getitem__(item)


class EnumTuple(tuple):

    def __init__(self, _, key):
        self._key = key

    def __new__(cls, iterable, key):
        return tuple.__new__(cls, iterable)
    
    def __contains__(self, item):
        if isinstance(item, EnumItem):

            item = getattr(item, self._key)

        return super().__contains__(item)

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, tuple(self))


class EnumItem(tuple):
    """
    枚举定义
    """
    def __init__(self, value, message, weight=None):

        self.__value = value
        self.__message = message
        self.__weight = weight

    def __new__(cls, value, message, weight=None):
        return tuple.__new__(cls, [value, message, weight])

    @property
    def value(self):
        return self.__value

    @property
    def message(self):
        return self.__message

    @property
    def weight(self):
        return self.__weight

    def __eq__(self, other):
        return self.__value == other

    def __ne__(self, other):
        return self.__value != other


class EnumItems(object):
    __slots__ = [
        'items',
        'value_list',
        'weight_list',
        'message_list',
        'value_weight',
        'weight_value',
        'value_message',
        'message_value',
        'ordered_weight_values',
        'ordered_weight_items'
    ]

    def __init__(self, *enum_tuples):

        data = _create_rela(enum_tuples)
        setattr(self, 'items', data['items'])
        setattr(self, 'value_list', data['value_list'])
        setattr(self, 'weight_list', data['weight_list'])
        setattr(self, 'message_list', data['message_list'])
        setattr(self, 'value_weight', data['value_weight'])
        setattr(self, 'weight_value', data['weight_value'])
        setattr(self, 'value_message', data['value_message'])
        setattr(self, 'message_value', data['message_value'])
        setattr(self, 'ordered_weight_items', data['ordered_weight_items'])
        setattr(self, 'ordered_weight_values', data['ordered_weight_values'])

    def __contains__(self, item):
        if isinstance(item, EnumItem):
            return item in self.items
        else:
            return item in self.value_list

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, self.items)


def _create_rela(enum_tuples):
    _value_list = []
    _weight_list = []
    _message_list = []

    _value_weight = {}
    _value_message = {}
    _message_value = {}
    _weight_value = {}
    _items = []
    _no_weight_items = []
    _has_weight_items = []
    for v in enum_tuples:
        if not isinstance(v, EnumItem):
            continue
        if v.value in _value_list:
            raise ValueError('not allow repeat value "{}"'.format(v.value))
        if v.message in _message_list:
            raise ValueError('not allow repeat message "{}"'.format(v.message))

        if v.weight is not None:
            _weight_value[v.weight] = v.value
            _weight_list.append(v.weight)
            _has_weight_items.append(v)
        else:
            _no_weight_items.append(v)

        _items.append(v)
        _value_list.append(v.value)
        _message_list.append(v.message)

        _value_message[v.value] = v.message
        _message_value[v.message] = v.value
        _value_weight[v.value] = v.weight

    _has_weight_items.sort(key=lambda x: x.weight)
    ordered_items = _has_weight_items + _no_weight_items
    data = {
        'items': tuple(_items),
        'value_list': EnumTuple(_value_list, 'value'),
        'weight_list': EnumTuple(_weight_list, 'weight'),
        'message_list': EnumTuple(_message_list, 'message'),
        'value_weight': EnumDict(_value_weight),
        'weight_value': EnumDict(_weight_value),
        'value_message': EnumDict(_value_message),
        'message_value': EnumDict(_message_value),
        'ordered_weight_items': tuple(ordered_items),
        'ordered_weight_values': tuple([item.value for item in ordered_items])
    }
    return data


class EnumMetaClass(type):
    """
    枚举元类
    """
    def __init__(cls, _what, _bases, _dict):

        enum_tuples = [val for val in _dict.values() if isinstance(val, EnumItem)]
        data = _create_rela(enum_tuples)

        setattr(cls, 'items', data['items'])
        setattr(cls, 'value_list', data['value_list'])
        setattr(cls, 'weight_list', data['weight_list'])
        setattr(cls, 'message_list', data['message_list'])
        setattr(cls, 'value_weight', data['value_weight'])
        setattr(cls, 'weight_value', data['weight_value'])
        setattr(cls, 'value_message', data['value_message'])
        setattr(cls, 'message_value', data['message_value'])
        setattr(cls, 'ordered_weight_items', data['ordered_weight_items'])
        setattr(cls, 'ordered_weight_values', data['ordered_weight_values'])

        super(EnumMetaClass, cls).__init__(_what, _bases, _dict)


class BaseEnum(metaclass=EnumMetaClass):
    """枚举基类"""
    __slots__ = [
        'items',
        'value_list',
        'weight_list',
        'message_list',
        'value_weight',
        'weight_value',
        'value_message',
        'message_value',
        'ordered_weight_values',
        'ordered_weight_items'
    ]

    @classmethod
    def get_message(cls, value, default=''):
        if isinstance(value, EnumItem):
            value = value.value
        return cls.value_message.get(value, default)

    @classmethod
    def get_weight(cls, value, default=0):
        if isinstance(value, EnumItem):
            value = value.value
        return cls.value_weight.get(value, default)


if __name__ == '__main__':

    class BoolEnum(BaseEnum):

        Yes = EnumItem(1, 'ok')
        No = EnumItem(0, 'no', 200)

        Range = EnumItems(Yes, No)


    print(BoolEnum.No)
    print(BoolEnum.No.value)
    print(BoolEnum.value_list)
    print(BoolEnum.message_list)
    print(BoolEnum.weight_list)
    print(BoolEnum.value_message)
    print(BoolEnum.value_weight)
    print(BoolEnum.message_value)
    print(BoolEnum.weight_value)
    print(BoolEnum.ordered_weight_values)
    print(BoolEnum.ordered_weight_items)
    print(BoolEnum.Yes == 1)
    print(BoolEnum.Yes == (1, 'ok'))
    print(BoolEnum.Range)
    print(BoolEnum.Range.value_message[BoolEnum.Yes])
    print(BoolEnum.Yes in BoolEnum.Range)
    print(BoolEnum.Yes in BoolEnum.value_list)
    print(BoolEnum.Yes in BoolEnum.message_list)
    print(BoolEnum.Yes in BoolEnum.weight_list)
    print(BoolEnum.Yes in BoolEnum.items)
    print(BoolEnum.Yes in BoolEnum.ordered_weight_items)
    print(BoolEnum.Yes in BoolEnum.ordered_weight_values)
